package com.panfeng.xcloud.scheduler.util.tron;

import com.github.ki5fpl.tronj.abi.TypeReference;
import com.github.ki5fpl.tronj.abi.datatypes.Address;
import com.github.ki5fpl.tronj.abi.datatypes.Bool;
import com.github.ki5fpl.tronj.abi.datatypes.Function;
import com.github.ki5fpl.tronj.abi.datatypes.generated.Uint256;
import org.bouncycastle.jcajce.provider.digest.SHA256;
import org.tron.protos.Protocol;
import org.tron.protos.contract.SmartContractOuterClass;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;

/**
 * @author: sunlight
 * @date: 2020/9/7 17:45
 */
public class Trc20TransferBuilder implements TransactionDataBuilder {
    private String ownerAddress;
    private String receiverAddress;
    private BigDecimal value;
    private  String contractAddress;
    private Long feeLimit;

    private Trc20TransferBuilder() {
    }

    public Trc20TransferBuilder(String ownerAddress, String receiverAddress, BigDecimal value,
                                String contractAddress, Long feeLimit) {
        this.ownerAddress = ownerAddress;
        this.receiverAddress = receiverAddress;
        this.value = value;
        this.contractAddress=contractAddress;
        this.feeLimit=feeLimit;
    }

    @Override
    public byte[] build() throws Exception {
        Protocol.Transaction transaction = createTransferTrc20Transaction(feeLimit);
        return transaction.toByteArray();
    }


    private Protocol.Transaction createTransferTrc20Transaction(Long feeLimit) throws Exception {
        return TxCreator.createTransaction(
                createTransferTrc20Builder(),
                Protocol.Transaction.Contract.ContractType.TriggerSmartContract,
                feeLimit
        );
    }

    private SmartContractOuterClass.TriggerSmartContract.Builder createTransferTrc20Builder() throws Exception {

        Function trc20Transfer = new Function("transfer",
                Arrays.asList(new Address(receiverAddress),
                        new Uint256(TokenConverter.tokenBigDecimalToBigInteger(value,Trc20Helper.decimals(ownerAddress,contractAddress)))),
                Arrays.asList(new TypeReference<Bool>() {
                }));
        return Trc20ParamBuilder.build(trc20Transfer, ownerAddress,contractAddress);
    }




    public  byte[] base58ToBytes(String s) {
        byte[] concat = base58ToRawBytes(s);
        byte[] data = Arrays.copyOf(concat, concat.length - 4);
        byte[] hash = Arrays.copyOfRange(concat, concat.length - 4, concat.length);

        SHA256.Digest digest = new SHA256.Digest();
        digest.update(data);
        byte[] hash0 = digest.digest();
        digest.reset();
        digest.update(hash0);

        byte[] rehash = Arrays.copyOf(digest.digest(), 4);
        if (!Arrays.equals(rehash, hash))
            throw new IllegalArgumentException("Checksum mismatch");
        return data;
    }

    static byte[] base58ToRawBytes(String s) {
        // Parse base-58 string
        BigInteger num = BigInteger.ZERO;
        String ALPHABET="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
        BigInteger ALPHABET_SIZE=BigInteger.valueOf(ALPHABET.length());
        for (int i = 0; i < s.length(); i++) {
            num = num.multiply(ALPHABET_SIZE);
            int digit = ALPHABET.indexOf(s.charAt(i));
            if (digit == -1)
                throw new IllegalArgumentException("Invalid character for Base58Check");
            num = num.add(BigInteger.valueOf(digit));
        }

        // Strip possible leading zero due to mandatory sign bit
        byte[] b = num.toByteArray();
        if (b[0] == 0)
            b = Arrays.copyOfRange(b, 1, b.length);

        try {
            // Convert leading '1' characters to leading 0-value bytes
            ByteArrayOutputStream buf = new ByteArrayOutputStream();
            for (int i = 0; i < s.length() && s.charAt(i) == ALPHABET.charAt(0); i++) buf.write(0);
            buf.write(b);
            return buf.toByteArray();
        } catch (IOException e) {
            throw new AssertionError(e);
        }
    }

}
